package com.sheng.leetcode.year2023.month11.day14;

import org.junit.Test;

import java.util.Arrays;

/**
 * @author by ls
 * @date 2023/11/14
 * <p>
 * 1334. 阈值距离内邻居最少的城市<p>
 * <p>
 * 有 n 个城市，按从 0 到 n-1 编号。给你一个边数组 edges，其中 edges[i] = [fromi, toi, weighti]<p>
 * 代表 fromi 和 toi 两个城市之间的双向加权边，距离阈值是一个整数 distanceThreshold。<p>
 * 返回能通过某些路径到达其他城市数目最少、且路径距离 最大<p>
 * 为 distanceThreshold 的城市。如果有多个这样的城市，则返回编号最大的城市。<p>
 * 注意，连接城市 i 和 j 的路径的距离等于沿该路径的所有边的权重之和。<p>
 * <p>
 * 示例 1：<p>
 * 输入：n = 4, edges = [[0,1,3],[1,2,1],[1,3,4],[2,3,1]], distanceThreshold = 4<p>
 * 输出：3<p>
 * 解释：城市分布图如上。<p>
 * 每个城市阈值距离 distanceThreshold = 4 内的邻居城市分别是：<p>
 * 城市 0 -> [城市 1, 城市 2]<p>
 * 城市 1 -> [城市 0, 城市 2, 城市 3]<p>
 * 城市 2 -> [城市 0, 城市 1, 城市 3]<p>
 * 城市 3 -> [城市 1, 城市 2]<p>
 * 城市 0 和 3 在阈值距离 4 以内都有 2 个邻居城市，但是我们必须返回城市 3，因为它的编号最大。<p>
 * <p>
 * 示例 2：<p>
 * 输入：n = 5, edges = [[0,1,2],[0,4,8],[1,2,3],[1,4,2],[2,3,1],[3,4,1]], distanceThreshold = 2<p>
 * 输出：0<p>
 * 解释：城市分布图如上。<p>
 * 每个城市阈值距离 distanceThreshold = 2 内的邻居城市分别是：<p>
 * 城市 0 -> [城市 1]<p>
 * 城市 1 -> [城市 0, 城市 4]<p>
 * 城市 2 -> [城市 3, 城市 4]<p>
 * 城市 3 -> [城市 2, 城市 4]<p>
 * 城市 4 -> [城市 1, 城市 2, 城市 3]<p>
 * 城市 0 在阈值距离 2 以内只有 1 个邻居城市。<p>
 * <p>
 * 提示：<p>
 * 2 <= n <= 100<p>
 * 1 <= edges.length <= n * (n - 1) / 2<p>
 * edges[i].length == 3<p>
 * 0 <= fromi < toi < n<p>
 * 1 <= weighti, distanceThreshold <= 10^4<p>
 * 所有 (fromi, toi) 都是不同的。<p>
 */
public class LeetCode1334 {

    @Test
    public void test01() {
//        int n = 4, distanceThreshold = 4;
//        int[][] edges = {{0, 1, 3}, {1, 2, 1}, {1, 3, 4}, {2, 3, 1}};
        int n = 5, distanceThreshold = 2;
        int[][] edges = {{0, 1, 2}, {0, 4, 8}, {1, 2, 3}, {1, 4, 2}, {2, 3, 1}, {3, 4, 1}};
        System.out.println(new Solution().findTheCity(n, edges, distanceThreshold));
    }
}

class Solution {
    public int findTheCity(int n, int[][] edges, int distanceThreshold) {
        // 返回能通过某些路径到达其他城市数目最少、且路径距离 最大
        // w[i][j] 代表从城市 i 能直接到达城市 j 的距离
        int[][] w = new int[n][n];
        for (int[] row : w) {
            // 防止加法溢出
            Arrays.fill(row, Integer.MAX_VALUE / 2);
        }
        // 将城市的边长权重赋值给 w
        for (int[] e : edges) {
            int x = e[0], y = e[1], wt = e[2];
            w[x][y] = w[y][x] = wt;
        }
        // 获取符合要求的城市
        int ans = 0;
        // 能到达城市最少时的最大计数
        int minCnt = n;
        for (int i = 0; i < n; i++) {
            // cnt 代表从城市 i 开始的能到达的城市数量
            int cnt = 0;
            for (int j = 0; j < n; j++) {
                // 判断 i 能否到达 j，并且满足路径长度小于等于 distanceThreshold
                if (j != i && dfs(n - 1, i, j, w) <= distanceThreshold) {
                    cnt++;
                }
            }
            // 相等时取最大的 i
            if (cnt <= minCnt) {
                minCnt = cnt;
                ans = i;
            }
        }
        return ans;
    }

    /**
     * 获取从城市 i 到城市 j 的距离
     *
     * @param k
     * @param i 起点城市的坐标
     * @param j 终点城市的坐标
     * @param w 城市之间的路径权重
     * @return 获取从城市 i 到城市 j 的距离
     */
    private int dfs(int k, int i, int j, int[][] w) {
        // 递归边界
        if (k < 0) {
            return w[i][j];
        }
        return Math.min(dfs(k - 1, i, j, w), dfs(k - 1, i, k, w) + dfs(k - 1, k, j, w));
    }
}
